use std::time::Instant;

use crate::sm3::error::Error;
use crate::sm3::compressor::Compressor;
use crate::sm3::padded_string_buffer::PaddedStringBuffer;
use crate::sm3::padded_file_buffer::PaddedFileBuffer;


pub struct SM3Digest{
    v:[u32;8],
    t:[u32;64],
}



impl SM3Digest{
    pub fn new()->Self{
        Self {
            v:[		
                0x7380166F,0x4914B2B9,0x172442D7,0xDA8A0600,0xA96F30BC,
			    0x163138AA,0xE38DEE4D,0xB0FB0E4E
            ],
            t:[
                0x79CC4519,0x79CC4519,0x79CC4519,0x79CC4519,0x79CC4519,
                0x79CC4519,0x79CC4519,0x79CC4519,0x79CC4519,0x79CC4519,
                0x79CC4519,0x79CC4519,0x79CC4519,0x79CC4519,0x79CC4519,
                0x79CC4519,0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,
                0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,
                0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,
                0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,
                0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,
                0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,
                0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,
                0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,
                0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A,
                0x7A879D8A,0x7A879D8A,0x7A879D8A,0x7A879D8A
            ]
        }
    }


    pub fn hash_string(&mut self,buffer_str:&str)->Result<[u32;8],Error>{
        let mut padded_buffer = PaddedStringBuffer::new(buffer_str.as_bytes());
        while let Some(frame) = padded_buffer.iter_one_frame_copy() {
            let mut compressor = Compressor::new(&frame);
            compressor.hash(&mut self.v,&self.t).unwrap();
        }

        Ok(self.v)
    }

    pub fn hash_file(&mut self,file_path:&str)->Result<[u32;8],Error>{
        let padded_buffer = PaddedFileBuffer::new(file_path);
        if padded_buffer.is_err(){
            return Err(Error::new(String::from("Filed to open file!")));
        }
        let mut padded_buffer = padded_buffer.unwrap();

        let _start = Instant::now();
        let mut _counter = 0;
        while let Some(frame) = padded_buffer.iter_one_frame_copy() {

            let mut compressor = Compressor::new(&frame);
            compressor.hash(&mut self.v,&self.t).unwrap();
            _counter +=1;
        }

        #[cfg(feature="print_time")]
        println!("hash used:{} ms, total:{} frames",_start.elapsed().as_millis(),_counter);
        Ok(self.v)
    }



}



#[cfg(test)]
mod tests{
    use super::*;
    use std::io::Write;

    //abc     616263
    //66c7f0f4 62eeedd9 d1f2d46b dc10e4e2 4167c487 5cf2f7a2 297da02b 8f4ba8e0
    #[test]
    fn test_hash_string_case1(){
        let test:[u8;3] = [0x61,0x62,0x63];
        let expect = [0x66c7f0f4,0x62eeedd9,0xd1f2d46b,0xdc10e4e2,0x4167c487,0x5cf2f7a2,0x297da02b,0x8f4ba8e0];
        let mut sm3 = SM3Digest::new();
        let digest_buffer = sm3.hash_string(&String::from_utf8(test.to_vec()).unwrap()).unwrap();
        assert_eq!(expect,digest_buffer);
    }

    //abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijab    62bytes = 496 bits
    //475D0246 40260F93 C4EC3D9F FB6B220F 3BFAC881 B032EB72 541CD9B4 F7F3C1E9
    #[test]
    fn test_hash_string_case2(){
        let test:[u8;62] = [
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62
                ];
        let expect = [0x475D0246,0x40260F93,0xC4EC3D9F,0xFB6B220F,0x3BFAC881,0xB032EB72,0x541CD9B4,0xF7F3C1E9];
        let mut sm3 = SM3Digest::new();
        let digest_buffer = sm3.hash_string(&String::from_utf8(test.to_vec()).unwrap()).unwrap();
        assert_eq!(expect,digest_buffer);
    }

    //abcd*16     61626364*16
    //debe9ff9 2275b8a1 38604889 c18e5a4d 6fdb70e5 387e5765 293dcba3 9c0c5732
    #[test]
    fn test_hash_string_case3(){
        let test:[u8;64] = [
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64
            ];
        let expect = [0xdebe9ff9,0x2275b8a1,0x38604889,0xc18e5a4d,0x6fdb70e5,0x387e5765,0x293dcba3,0x9c0c5732];
        let mut sm3 = SM3Digest::new();
        let digest_buffer = sm3.hash_string(&String::from_utf8(test.to_vec()).unwrap()).unwrap();
        assert_eq!(expect,digest_buffer);
    }

    //abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdef    62bytes = 496 bits
    //59664715 ea5205fe 5b98bf87 35a41aae 553150a8 018e6874 7752bd9f e321a2f0
    #[test]
    fn test_hash_string_case4(){
        let test:[u8;62] = [
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66
                ];
        let expect = [0x59664715 ,0xea5205fe ,0x5b98bf87 ,0x35a41aae ,0x553150a8 ,0x018e6874 ,0x7752bd9f ,0xe321a2f0];
        let mut sm3 = SM3Digest::new();
        let digest_buffer = sm3.hash_string(&String::from_utf8(test.to_vec()).unwrap()).unwrap();
        assert_eq!(expect,digest_buffer);
    }



    //abc     616263
    //66c7f0f4 62eeedd9 d1f2d46b dc10e4e2 4167c487 5cf2f7a2 297da02b 8f4ba8e0
    #[test]
    fn test_hash_file_case1(){
        let mut file_test = std::fs::File::create("./test_case1").unwrap();
        let test:[u8;3] = [0x61,0x62,0x63];
        file_test.write(&test).unwrap();
        let expect = [0x66c7f0f4,0x62eeedd9,0xd1f2d46b,0xdc10e4e2,0x4167c487,0x5cf2f7a2,0x297da02b,0x8f4ba8e0];
        let mut sm3 = SM3Digest::new();
        let digest_buffer = sm3.hash_file("./test_case1").unwrap();
        std::fs::remove_file("./test_case1").unwrap();

        assert_eq!(expect,digest_buffer);

    }

    //abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijab    62bytes = 496 bits
    //475D0246 40260F93 C4EC3D9F FB6B220F 3BFAC881 B032EB72 541CD9B4 F7F3C1E9
    #[test]
    fn test_hash_file_case2(){
        let mut file_test = std::fs::File::create("./test_case2").unwrap();
        let test:[u8;62] = [
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
                0x61,0x62
                ];
        file_test.write(&test).unwrap();
        let expect = [0x475D0246,0x40260F93,0xC4EC3D9F,0xFB6B220F,0x3BFAC881,0xB032EB72,0x541CD9B4,0xF7F3C1E9];
        let mut sm3 = SM3Digest::new();
        let digest_buffer = sm3.hash_file("./test_case2").unwrap();
        std::fs::remove_file("./test_case2").unwrap();
        assert_eq!(expect,digest_buffer);
    }

    //abcd*16     61626364*16
    //debe9ff9 2275b8a1 38604889 c18e5a4d 6fdb70e5 387e5765 293dcba3 9c0c5732
    #[test]
    fn test_hash_file_case3(){
        let mut file_test = std::fs::File::create("./test_case3").unwrap();
        let test:[u8;64] = [
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64,
            0x61,0x62,0x63,0x64
            ];
        file_test.write(&test).unwrap();
        let expect = [0xdebe9ff9,0x2275b8a1,0x38604889,0xc18e5a4d,0x6fdb70e5,0x387e5765,0x293dcba3,0x9c0c5732];
        let mut sm3 = SM3Digest::new();
        let digest_buffer = sm3.hash_file("./test_case3").unwrap();
        std::fs::remove_file("./test_case3").unwrap();
        assert_eq!(expect,digest_buffer);
    }

    //abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdef    62bytes = 496 bits
    //59664715 ea5205fe 5b98bf87 35a41aae 553150a8 018e6874 7752bd9f e321a2f0
    #[test]
    fn test_hash_file_case4(){
        let mut file_test = std::fs::File::create("./test_case4").unwrap();
        let test:[u8;62] = [
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
                0x61,0x62,0x63,0x64,0x65,0x66
                ];
        file_test.write(&test).unwrap();
        let expect = [0x59664715 ,0xea5205fe ,0x5b98bf87 ,0x35a41aae ,0x553150a8 ,0x018e6874 ,0x7752bd9f ,0xe321a2f0];
        let mut sm3 = SM3Digest::new();
        let digest_buffer = sm3.hash_file("./test_case4").unwrap();
        std::fs::remove_file("./test_case4").unwrap();
        assert_eq!(expect,digest_buffer);
    }


    //testfile:./test_file    
    //85D931E9 C2190B3B D0F41AF1 C9F148BD 3E708D96 802D7B8D A1E33E2B 2087F4C8
    #[test]
    fn test_hash_file_case5(){
        let expect = [0x85D931E9,0xC2190B3B,0xD0F41AF1,0xC9F148BD,0x3E708D96,0x802D7B8D,0xA1E33E2B,0x2087F4C8 ];
        let mut sm3 = SM3Digest::new();
        let digest_buffer = sm3.hash_file("./test_file").unwrap();
        assert_eq!(expect,digest_buffer);
    }




}






